package com.woniuxy.gateway.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.woniuxy.utils.JWTUtil;
import com.woniuxy.utils.ResponseResult;
import com.woniuxy.utils.ResultStatus;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Resource
    RedisTemplate<String,Object> redisTemplate;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取request     此request非HttpServletRequest
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //获取URL
        String uri = request.getURI().getPath();
        //判断uri是否需要认证才能访问
        if (validate(uri)) {
            //不需要认证，放行
            return chain.filter(exchange);
        }
        //获取头信息
        String token = null;
        List<String> auths = request.getHeaders().get("authentication");
        if (auths != null) {
            token = auths.get(0);
        }
        String refresh = null;
        List<String> refreshs = request.getHeaders().get("refresh");
        if (refreshs != null) {
            refresh = refreshs.get(0);
        }
        //判断token、refresh是否为null
        if (token == null || refresh == null) {
            return result(response);
        }
        //执行到此处说明token、refresh都不为空，只能说明携带了，还需要对它们进行校验
        //校验refresh:判断redis中是否有这个key，没有，表示refresh为伪造或过期，需要重新登录
        if (!redisTemplate.hasKey(refresh)) {
            //不含refresh
            return result(response);
        }
        //校验token
        switch (JWTUtil.verify(token)) {
            case TOKEN_BAD:
                return result(response);
            case TOKEN_EXPIRE:
                //重新生成token，并返回给浏览器
                token = JWTUtil.generateToken(JWTUtil.getUname(token));
                //将token设置给响应头
                response.getHeaders().add("authentication", token);
                response.getHeaders().add("refresh", refresh);
                //更新redis
                break;
        }
        //更新redis中key的过期时间
        redisTemplate.expire(refresh, JWTUtil.REFRESH_TOKEN_EXPIRE_TIME, TimeUnit.MILLISECONDS);
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }

    private Mono<Void> result(ServerHttpResponse response) {
        //假设当前用户的操作需要认证，而且还没有认证，应该给前端返回需要认证
        ResponseResult result = new ResponseResult();
        result.setCode(500);
        result.setMessage("请登录");
        result.setStatus(ResultStatus.NO_LOGIN);
        //需要将返回的对象转换成byte类型数组
        byte[] data = null;
        try {
            data = new ObjectMapper().writeValueAsString(result).getBytes(StandardCharsets.UTF_8);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        //创建buffer对象
        DataBuffer dataBuffer = response.bufferFactory().wrap(data);
        //设置响应头
        response.getHeaders().add("Content-Type", "application/json;charset=utf-8");
        //返回数据:请求被终止
        return response.writeWith(Mono.just(dataBuffer));
    }

    /**
     * 校验uri是否需要进行认证才能访问
     *
     * @param uri
     * @return
     */
    private boolean validate(String uri) {
        List<String> anon = Arrays.asList(
                "/user/login"
        );
        for (String s : anon) {
            if (uri.startsWith(s)){
                return true;
            }
        }
        return false;
    }
}
